//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Diagnostics;

namespace Microsoft.Research.eResearch.Blackboard.Common
{
    /// <summary>
    /// Exception class for exceptions related to errors in policy
    /// settings
    /// </summary>
    public class PolicyException : Exception
    {
        /// <summary>
        /// Policy Exception Constuctor
        /// </summary>
        /// <param name="msg"></param>
        public PolicyException(string msg)
            : base(msg)
        { }
    }

    /// <summary>
    /// Types of serialization supported
    /// </summary>
    public enum SerializtionMethodTypes
    {
        ToString,

        XmlSerialize
    }

    /// <summary>
    /// Types of size calculation methods supported
    /// </summary>
    public enum SizeMethod
    {
        String,

        Binary
    }

    /// <summary>
    /// Class for storing the serializtion settings
    /// </summary>
    public class SerializationSettings
    {
        #region Public Properties

        /// <summary>
        /// Max Size of the message in KB
        /// </summary>
        public int MessageSize
        {
            get { return this.messageSize; }
        }

        /// <summary>
        /// Method to use for calculating the message size.
        /// </summary>
        public SizeMethod MessageSizeMethod
        {
            get { return this.messageSizeMethod; }
        }

        /// <summary>
        /// Method used for serialization
        /// </summary>
        public SerializtionMethodTypes SerializationMethod
        {
            get { return this.serializationMethod; }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// This method reads the serialization settings from the config file
        /// and sets the private members. If the settins does not contain the 
        /// section for serializtion then the default settings are left as is.
        /// </summary>
        /// <param name="settings">Xml document representing the settings xml</param>
        public void ReadSettings(XmlDocument settings)
        {
            if (null == settings || string.IsNullOrEmpty(settings.OuterXml))
                return;

            // Try loading the serialization section
            XmlNodeList serializationNodes = settings.GetElementsByTagName("ParameterSerialization");
            if (null == serializationNodes)
                return;

            // If there are more than one nodes for Serialization ignore those.
            XmlNode serializationNode = serializationNodes[0];

            if (null != serializationNode.Attributes["MaxMessageSize"])
            {
                try
                {
                    int maxMessageSize = Convert.ToInt32(serializationNode.Attributes["MaxMessageSize"].Value);
                    this.messageSize = maxMessageSize > 8 ? 8 : maxMessageSize;
                }
                catch (Exception)
                {
                }
            }

            if (null != serializationNode.Attributes["MessageSizeMethod"])
            {
                if (serializationNode.Attributes["MessageSizeMethod"].Value == "Binary")
                    this.messageSizeMethod = SizeMethod.Binary;
                else if (serializationNode.Attributes["MessageSizeMethod"].Value == "String")
                    this.messageSizeMethod = SizeMethod.String;
            }

            if (null != serializationNode.Attributes["Method"])
            {
                if (serializationNode.Attributes["Method"].Value == "String")
                    this.serializationMethod = SerializtionMethodTypes.ToString;
                else if (serializationNode.Attributes["Method"].Value == "Xml")
                    this.serializationMethod = SerializtionMethodTypes.XmlSerialize;
            }

            foreach (XmlNode child in serializationNode.ChildNodes)
            {
                string methodName = child.Attributes["Method"].Value;
                SerializtionMethodTypes serType = SerializtionMethodTypes.ToString;
                if (methodName == "String")
                    serType = SerializtionMethodTypes.ToString;
                else if (methodName == "Xml")
                    serType = SerializtionMethodTypes.XmlSerialize;

                serializedTypes.Add(child.Attributes["Type"].Value, serType);
            }
        }

        /// <summary>
        /// This method looks up the dictionary to see if this type has an 
        /// associated serialization method. If not found the default method
        /// is returned.
        /// </summary>
        /// <param name="objType"></param>
        /// <returns></returns>
        public SerializtionMethodTypes GetSerializationMethod(Type objType)
        {
            string typeString = objType.ToString();
            if (this.serializedTypes.ContainsKey(typeString))
                return this.serializedTypes[typeString];
            return this.SerializationMethod;
        }

        #endregion

        #region Private Data

        private int messageSize = 8;

        private SizeMethod messageSizeMethod = SizeMethod.String;

        private SerializtionMethodTypes serializationMethod = SerializtionMethodTypes.ToString;

        private Dictionary<string, SerializtionMethodTypes> serializedTypes =
            new Dictionary<string, SerializtionMethodTypes>();

        #endregion
    }

    /// <summary>
    /// Exception details to be included in exception messages.
    /// </summary>
    public enum ExceptionDetails
    {
        Serialize = 2,

        ExceptionMessage = 4,

        StackTrace = 8,

        ExceptionType = 16

    }

    /// <summary>
    /// Class for storing details about how to report exceptions
    /// from WF's
    /// </summary>
    public class ExceptionSettings
    {

        public int ExceptionFlags
        {
            get { return this.exceptionFlags; }
        }

        /// <summary>
        /// This method reads the exception details from the settings file and
        /// stores the settings in the private integer.
        /// </summary>
        /// <param name="settings"></param>
        public void ReadSettings(XmlDocument settings)
        {
            if (null == settings || string.IsNullOrEmpty(settings.OuterXml))
                return;

            // Try loading the serialization section
            XmlNodeList exceptionNodes = settings.GetElementsByTagName("ExceptionHandling");
            if (null == exceptionNodes)
                return;

            // If there are more than one nodes for Exception ignore those.
            XmlNode exceptionNode = exceptionNodes[0];

            foreach (XmlNode node in exceptionNode.ChildNodes)
            {
                string detail = node.InnerText;

                if (detail == "Serialize")
                    this.exceptionFlags |= (int)ExceptionDetails.Serialize;
                else if (detail == "ExceptionMessage")
                    this.exceptionFlags |= (int)ExceptionDetails.ExceptionMessage;
                else if (detail == "StackTrace")
                    this.exceptionFlags |= (int)ExceptionDetails.StackTrace;
                else if (detail == "ExceptionType")
                    this.exceptionFlags |= (int)ExceptionDetails.ExceptionType;
            }
        }

        /// <summary>
        /// This method can be used to check if the settings contain the value
        /// for a detail.
        /// </summary>
        /// <param name="detail"></param>
        /// <returns></returns>
        public int IsDetailsIncluded(ExceptionDetails detail)
        {
            return (((int)detail) & this.exceptionFlags);
        }

        private int exceptionFlags = 0;
    }

    /// <summary>
    /// This class loads the concept names from the settings file.
    /// </summary>
    public class MessageConcepts
    {
        /// <summary>
        /// This method reads the concept names from the settings file.
        /// </summary>
        /// <param name="settings"></param>
        public void ReadSettings(XmlDocument settings)
        {
            if (null == settings || string.IsNullOrEmpty(settings.OuterXml))
                return;

            // Try loading the serialization section
            XmlNodeList conceptNodes = settings.GetElementsByTagName("Concepts");
            if (null == conceptNodes)
                return;

            // If there are more than one nodes for Concepts ignore those.
            XmlNode conceptNode = conceptNodes[0];

            foreach (XmlNode node in conceptNode)
            {
                conceptNames.Add(node.Attributes["Name"].Value);
            }
        }

        public bool IsConceptPresent(string conceptName)
        {
            return conceptNames.Contains(conceptName);
        }

        private List<string> conceptNames = new List<string>();

    }

    /// <summary>
    /// This class loads the blocked concept names from the settings file.
    /// </summary>
    public class MessageBlockedConcepts
    {
        /// <summary>
        /// Reads the settings file.
        /// </summary>
        /// <param name="settings">The settings.</param>
        public void ReadSettings(XmlDocument settings)
        {
            try
            {
                if (null == settings || string.IsNullOrEmpty(settings.OuterXml))
                    return;

                // Try loading the Message Blocked Concepts
                XmlNodeList blockedConceptNodes = settings.GetElementsByTagName("BlockedConcept");
                if (null == blockedConceptNodes)
                    return;

                foreach (XmlNode node in blockedConceptNodes)
                {
                    string conceptName = node.Attributes["Name"].Value;
                    if (!string.IsNullOrEmpty(conceptName) && !blockedConceptNames.Contains(conceptName))
                        blockedConceptNames.Add(conceptName);
                }
            }
            catch(Exception)
            {
            }
        }

        /// <summary>
        /// Determines whether [is concept blocked] [the specified concept name].
        /// </summary>
        /// <param name="conceptName">Name of the concept.</param>
        /// <returns>
        /// 	<c>true</c> if [is concept blocked] [the specified concept name]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsConceptBlocked(string conceptName)
        {
            return blockedConceptNames.Contains(conceptName);
        }

        /// <summary>
        /// List of blocked concepts from setting file.
        /// </summary>
        public List<string> blockedConceptNames = new List<string>();
    }
}
